return ret;
}
-/* adapt security state (running_types and conflict_aggregate_set) to all running
- * domains; chwall_init_state is called when a policy is changed to bring the security
- * information into a consistent state and to detect violations (return != 0).
- * from a security point of view, we simulate that all running domains are re-started
+/*
+ * Adapt security state (running_types and conflict_aggregate_set) to all
+ * running domains; chwall_init_state is called when a policy is changed
+ * to bring the security information into a consistent state and to detect
+ * violations (return != 0) from a security point of view, we simulate
+ * that all running domains are re-started
*/
static int
chwall_init_state(struct acm_chwall_policy_buffer *chwall_buf,
- domaintype_t * ssidrefs, domaintype_t * conflict_sets,
+ domaintype_t * ssidrefs,
+ domaintype_t * conflict_sets,
domaintype_t * running_types,
- domaintype_t * conflict_aggregate_set)
+ domaintype_t * conflict_aggregate_set,
+ struct acm_sized_buffer *errors /* may be NULL */)
{
int violation = 0, i, j;
struct chwall_ssid *chwall_ssid;
ssidref_t chwall_ssidref;
- struct domain *d;
+ struct acm_ssid_domain *rawssid;
+
+ read_lock(&ssid_list_rwlock);
- spin_lock(&domlist_update_lock);
/* go through all domains and adjust policy as if this domain was started now */
- for_each_domain ( d )
+ for_each_acmssid( rawssid )
{
chwall_ssid =
- GET_SSIDP(ACM_CHINESE_WALL_POLICY,
- (struct acm_ssid_domain *)d->ssid);
+ GET_SSIDP(ACM_CHINESE_WALL_POLICY, rawssid);
chwall_ssidref = chwall_ssid->chwall_ssidref;
traceprintk("%s: validating policy for domain %x (chwall-REF=%x).\n",
__func__, d->domain_id, chwall_ssidref);
printk("%s: CHINESE WALL CONFLICT in type %02x.\n",
__func__, i);
violation = 1;
+
+ acm_array_append_tuple(errors, ACM_CHWALL_CONFLICT, i);
+
goto out;
}
/* set violation and break out of the loop */
}
}
out:
- spin_unlock(&domlist_update_lock);
+ read_unlock(&ssid_list_rwlock);
return violation;
/* returning "violation != 0" means that the currently running set of domains would
* not be possible if the new policy had been enforced before starting them; for chinese
* more than one type is currently running */
}
-static int chwall_set_policy(u8 * buf, u32 buf_size, int is_bootpolicy)
+
+int
+do_chwall_init_state_curr(struct acm_sized_buffer *errors)
{
+ struct acm_chwall_policy_buffer chwall_buf = {
+ /* only these two are important */
+ .chwall_max_types = chwall_bin_pol.max_types,
+ .chwall_max_conflictsets = chwall_bin_pol.max_conflictsets,
+ };
+ /* reset running_types and aggregate set for recalculation */
+ memset(chwall_bin_pol.running_types,
+ 0x0,
+ sizeof(domaintype_t) * chwall_bin_pol.max_types);
+ memset(chwall_bin_pol.conflict_aggregate_set,
+ 0x0,
+ sizeof(domaintype_t) * chwall_bin_pol.max_types);
+ return chwall_init_state(&chwall_buf,
+ chwall_bin_pol.ssidrefs,
+ chwall_bin_pol.conflict_sets,
+ chwall_bin_pol.running_types,
+ chwall_bin_pol.conflict_aggregate_set,
+ errors);
+}
+
+/*
+ * Attempt to set the policy. This function must be called in test_only
+ * mode first to only perform checks. A second call then does the
+ * actual changes.
+ */
+static int _chwall_update_policy(u8 *buf, u32 buf_size, int test_only,
+ struct acm_sized_buffer *errors)
+{
+ int rc = -EFAULT;
/* policy write-locked already */
struct acm_chwall_policy_buffer *chwall_buf =
(struct acm_chwall_policy_buffer *) buf;
void *ssids = NULL, *conflict_sets = NULL, *running_types =
NULL, *conflict_aggregate_set = NULL;
- if (buf_size < sizeof(struct acm_chwall_policy_buffer))
- return -EINVAL;
-
- /* rewrite the policy due to endianess */
- chwall_buf->policy_code = be32_to_cpu(chwall_buf->policy_code);
- chwall_buf->policy_version = be32_to_cpu(chwall_buf->policy_version);
- chwall_buf->chwall_max_types = be32_to_cpu(chwall_buf->chwall_max_types);
- chwall_buf->chwall_max_ssidrefs =
- be32_to_cpu(chwall_buf->chwall_max_ssidrefs);
- chwall_buf->chwall_max_conflictsets =
- be32_to_cpu(chwall_buf->chwall_max_conflictsets);
- chwall_buf->chwall_ssid_offset = be32_to_cpu(chwall_buf->chwall_ssid_offset);
- chwall_buf->chwall_conflict_sets_offset =
- be32_to_cpu(chwall_buf->chwall_conflict_sets_offset);
- chwall_buf->chwall_running_types_offset =
- be32_to_cpu(chwall_buf->chwall_running_types_offset);
- chwall_buf->chwall_conflict_aggregate_offset =
- be32_to_cpu(chwall_buf->chwall_conflict_aggregate_offset);
-
- /* policy type and version checks */
- if ((chwall_buf->policy_code != ACM_CHINESE_WALL_POLICY) ||
- (chwall_buf->policy_version != ACM_CHWALL_VERSION))
- return -EINVAL;
-
- /* during boot dom0_chwall_ssidref is set */
- if (is_bootpolicy &&
- (dom0_chwall_ssidref >= chwall_buf->chwall_max_ssidrefs)) {
- goto error_free;
- }
-
/* 1. allocate new buffers */
ssids =
xmalloc_array(domaintype_t,
* this can fail if new policy is conflicting with running domains */
if (chwall_init_state(chwall_buf, ssids,
conflict_sets, running_types,
- conflict_aggregate_set))
+ conflict_aggregate_set,
+ errors))
{
printk("%s: New policy conflicts with running domains. Policy load aborted.\n",
__func__);
goto error_free; /* new policy conflicts with running domains */
}
+
+ /* if this was only a test run, exit with ACM_OK */
+ if (test_only) {
+ rc = ACM_OK;
+ goto error_free;
+ }
+
/* 4. free old policy buffers, replace with new ones */
chwall_bin_pol.max_types = chwall_buf->chwall_max_types;
chwall_bin_pol.max_ssidrefs = chwall_buf->chwall_max_ssidrefs;
return ACM_OK;
error_free:
- printk("%s: ERROR setting policy.\n", __func__);
+ if (!test_only) printk("%s: ERROR setting policy.\n", __func__);
xfree(ssids);
xfree(conflict_sets);
xfree(running_types);
xfree(conflict_aggregate_set);
- return -EFAULT;
+ return rc;
+}
+
+/*
+ * This function MUST be called before the chwall_ste_policy function!
+ */
+static int chwall_test_policy(u8 *buf, u32 buf_size, int is_bootpolicy,
+ struct acm_sized_buffer *errors)
+{
+ struct acm_chwall_policy_buffer *chwall_buf =
+ (struct acm_chwall_policy_buffer *) buf;
+
+ if (buf_size < sizeof(struct acm_chwall_policy_buffer))
+ return -EINVAL;
+
+ /* rewrite the policy due to endianess */
+ chwall_buf->policy_code = be32_to_cpu(chwall_buf->policy_code);
+ chwall_buf->policy_version = be32_to_cpu(chwall_buf->policy_version);
+ chwall_buf->chwall_max_types =
+ be32_to_cpu(chwall_buf->chwall_max_types);
+ chwall_buf->chwall_max_ssidrefs =
+ be32_to_cpu(chwall_buf->chwall_max_ssidrefs);
+ chwall_buf->chwall_max_conflictsets =
+ be32_to_cpu(chwall_buf->chwall_max_conflictsets);
+ chwall_buf->chwall_ssid_offset =
+ be32_to_cpu(chwall_buf->chwall_ssid_offset);
+ chwall_buf->chwall_conflict_sets_offset =
+ be32_to_cpu(chwall_buf->chwall_conflict_sets_offset);
+ chwall_buf->chwall_running_types_offset =
+ be32_to_cpu(chwall_buf->chwall_running_types_offset);
+ chwall_buf->chwall_conflict_aggregate_offset =
+ be32_to_cpu(chwall_buf->chwall_conflict_aggregate_offset);
+
+ /* policy type and version checks */
+ if ((chwall_buf->policy_code != ACM_CHINESE_WALL_POLICY) ||
+ (chwall_buf->policy_version != ACM_CHWALL_VERSION))
+ return -EINVAL;
+
+ /* during boot dom0_chwall_ssidref is set */
+ if (is_bootpolicy &&
+ (dom0_chwall_ssidref >= chwall_buf->chwall_max_ssidrefs))
+ return -EINVAL;
+
+
+ return _chwall_update_policy(buf, buf_size, 1, errors);
+}
+
+static int chwall_set_policy(u8 *buf, u32 buf_size)
+{
+ return _chwall_update_policy(buf, buf_size, 0, NULL);
}
static int chwall_dump_stats(u8 * buf, u16 len)
.init_domain_ssid = chwall_init_domain_ssid,
.free_domain_ssid = chwall_free_domain_ssid,
.dump_binary_policy = chwall_dump_policy,
+ .test_binary_policy = chwall_test_policy,
.set_binary_policy = chwall_set_policy,
.dump_statistics = chwall_dump_stats,
.dump_ssid_types = chwall_dump_ssid_types,
/* acm binary policy lock */
DEFINE_RWLOCK(acm_bin_pol_rwlock);
-/* ACM's only accepted policy name */
+/* ACM's only accepted policy name during boot */
char polname[80];
char *acm_accepted_boot_policy_name = NULL;
+/* a lits of all chained ssid structures */
+LIST_HEAD(ssid_list);
+DEFINE_RWLOCK(ssid_list_rwlock);
+
static void __init set_dom0_ssidref(const char *val)
{
/* expected format:
int i;
int dom0_ssidref = simple_strtoull(val, &c, 0);
- if (!strncmp(&c[0],":sHype:", 7)) {
+ if (!strncmp(&c[0],":ACM:", 5)) {
lo = dom0_ssidref & 0xffff;
if (lo < ACM_MAX_NUM_TYPES && lo >= 1)
dom0_chwall_ssidref = lo;
return rc;
rc = do_acm_set_policy((void *)policy_start, (u32)policy_len,
- is_bootpolicy);
+ is_bootpolicy,
+ NULL, NULL, NULL);
if (rc == ACM_OK)
{
printkd("Policy len 0x%lx, start at %p.\n",policy_len,policy_start);
return ACM_INIT_SSID_ERROR;
}
+ INIT_LIST_HEAD(&ssid->node);
ssid->datatype = ACM_DATATYPE_domain;
ssid->subject = subj;
- ssid->domainid = subj->domain_id;
+ ssid->domainid = subj->domain_id;
ssid->primary_ssid = NULL;
ssid->secondary_ssid = NULL;
acm_free_domain_ssid(ssid);
return ACM_INIT_SSID_ERROR;
}
+
+ write_lock(&ssid_list_rwlock);
+ list_add(&ssid->node, &ssid_list);
+ write_unlock(&ssid_list_rwlock);
+
printkd("%s: assigned domain %x the ssidref=%x.\n",
__func__, subj->domain_id, ssid->ssidref);
return ACM_OK;
if (acm_secondary_ops->free_domain_ssid != NULL)
acm_secondary_ops->free_domain_ssid(ssid->secondary_ssid);
ssid->secondary_ssid = NULL;
+
+ write_lock(&ssid_list_rwlock);
+ list_del(&ssid->node);
+ write_unlock(&ssid_list_rwlock);
+
xfree(ssid);
printkd("%s: Freed individual domain ssid (domain=%02x).\n",
__func__, id);
}
static int
-null_set_binary_policy(u8 *buf, u32 buf_size, int is_bootpolicy)
+null_test_binary_policy(u8 *buf, u32 buf_size, int is_bootpolicy,
+ struct acm_sized_buffer *errors)
+{
+ return ACM_OK;
+}
+
+static int
+null_set_binary_policy(u8 *buf, u32 buf_size)
{
return ACM_OK;
}
.init_domain_ssid = null_init_domain_ssid,
.free_domain_ssid = null_free_domain_ssid,
.dump_binary_policy = null_dump_binary_policy,
+ .test_binary_policy = null_test_binary_policy,
.set_binary_policy = null_set_binary_policy,
.dump_statistics = null_dump_stats,
.dump_ssid_types = null_dump_ssid_types,
/****************************************************************
* acm_policy.c
*
- * Copyright (C) 2005 IBM Corporation
+ * Copyright (C) 2005-2007 IBM Corporation
*
* Author:
* Reiner Sailer <sailer@watson.ibm.com>
#include <xen/delay.h>
#include <xen/sched.h>
#include <xen/guest_access.h>
+#include <public/xen.h>
#include <acm/acm_core.h>
#include <public/acm_ops.h>
#include <acm/acm_hooks.h>
#include <acm/acm_endian.h>
+#include <asm/current.h>
+
+static int acm_check_deleted_ssidrefs(struct acm_sized_buffer *dels,
+ struct acm_sized_buffer *errors);
+static void acm_doms_change_ssidref(ssidref_t (*translator)
+ (const struct acm_ssid_domain *,
+ const struct acm_sized_buffer *),
+ struct acm_sized_buffer *translation_map);
+static void acm_doms_restore_ssidref(void);
+static ssidref_t oldssid_to_newssid(const struct acm_ssid_domain *,
+ const struct acm_sized_buffer *map);
+
int
acm_set_policy(XEN_GUEST_HANDLE(void) buf, u32 buf_size)
printk("%s: Error copying!\n",__func__);
goto error_free;
}
- ret = do_acm_set_policy(policy_buffer, buf_size, 0);
+ ret = do_acm_set_policy(policy_buffer, buf_size, 0,
+ NULL, NULL, NULL);
error_free:
xfree(policy_buffer);
}
-int
-do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy)
+/*
+ * Update the policy of the running system by:
+ * - deleting ssidrefs that are not in the new policy anymore
+ * -> no running domain may use such an ssidref
+ * - assign new ssidrefs to domains based on their old ssidrefs
+ *
+ */
+static int
+_acm_update_policy(void *buf, u32 buf_size, int is_bootpolicy,
+ struct acm_policy_buffer *pol,
+ struct acm_sized_buffer *deletions,
+ struct acm_sized_buffer *ssidchanges,
+ struct acm_sized_buffer *errors)
{
- struct acm_policy_buffer *pol = (struct acm_policy_buffer *)buf;
uint32_t offset, length;
- /* some sanity checking */
- if ((be32_to_cpu(pol->magic) != ACM_MAGIC) ||
- (buf_size != be32_to_cpu(pol->len)) ||
- (be32_to_cpu(pol->policy_version) != ACM_POLICY_VERSION))
- {
- printk("%s: ERROR in Magic, Version, or buf size.\n", __func__);
- goto error_free;
+
+ write_lock(&acm_bin_pol_rwlock);
+
+ /*
+ first some tests to check compatibility of new policy with
+ current state of system/domains
+ */
+
+ /* if ssidrefs are to be deleted, make sure no domain is using them */
+ if (deletions != NULL) {
+ if (acm_check_deleted_ssidrefs(deletions, errors))
+ goto error_lock_free;
}
- if (acm_active_security_policy == ACM_POLICY_UNDEFINED) {
- /* setup the policy with the boot policy */
- if (acm_init_binary_policy((be32_to_cpu(pol->secondary_policy_code) << 4) |
- be32_to_cpu(pol->primary_policy_code))) {
- goto error_free;
- }
- acm_active_security_policy =
- (acm_bin_pol.secondary_policy_code << 4) | acm_bin_pol.primary_policy_code;
+ if ((ssidchanges != NULL) && (ssidchanges->num_items > 0)) {
+ /* assign all running domains new ssidrefs as requested */
+ acm_doms_change_ssidref(oldssid_to_newssid, ssidchanges);
}
- /* once acm_active_security_policy is set, it cannot be changed */
- if ((be32_to_cpu(pol->primary_policy_code) != acm_bin_pol.primary_policy_code) ||
- (be32_to_cpu(pol->secondary_policy_code) != acm_bin_pol.secondary_policy_code))
- {
- printkd("%s: Wrong policy type in boot policy!\n", __func__);
- goto error_free;
+ /* test primary policy data with the new ssidrefs */
+ offset = be32_to_cpu(pol->primary_buffer_offset);
+ length = be32_to_cpu(pol->secondary_buffer_offset) - offset;
+
+ if ( (offset + length) > buf_size ||
+ acm_primary_ops->test_binary_policy(buf + offset, length,
+ is_bootpolicy,
+ errors))
+ goto error_lock_free;
+
+ /* test secondary policy data with the new ssidrefs */
+ offset = be32_to_cpu(pol->secondary_buffer_offset);
+ length = be32_to_cpu(pol->len) - offset;
+ if ( (offset + length) > buf_size ||
+ acm_secondary_ops->test_binary_policy(buf + offset, length,
+ is_bootpolicy,
+ errors)) {
+ goto error_lock_free;
}
- /* get bin_policy lock and rewrite policy (release old one) */
- write_lock(&acm_bin_pol_rwlock);
+ /* end of testing --- now real updates */
offset = be32_to_cpu(pol->policy_reference_offset);
length = be32_to_cpu(pol->primary_buffer_offset) - offset;
/* set label reference name */
if ( (offset + length) > buf_size ||
- acm_set_policy_reference(buf + offset, length))
+ acm_set_policy_reference(buf + offset, length) )
goto error_lock_free;
/* set primary policy data */
offset = be32_to_cpu(pol->primary_buffer_offset);
length = be32_to_cpu(pol->secondary_buffer_offset) - offset;
- if ( (offset + length) > buf_size ||
- acm_primary_ops->set_binary_policy(buf + offset, length,
- is_bootpolicy))
+ if ( acm_primary_ops->set_binary_policy(buf + offset, length) )
goto error_lock_free;
/* set secondary policy data */
offset = be32_to_cpu(pol->secondary_buffer_offset);
length = be32_to_cpu(pol->len) - offset;
- if ( (offset + length) > buf_size ||
- acm_secondary_ops->set_binary_policy(buf + offset, length,
- is_bootpolicy))
+ if ( acm_secondary_ops->set_binary_policy(buf + offset, length) )
goto error_lock_free;
memcpy(&acm_bin_pol.xml_pol_version,
write_unlock(&acm_bin_pol_rwlock);
return ACM_OK;
- error_lock_free:
+error_lock_free:
+ if ((ssidchanges != NULL) && (ssidchanges->num_items > 0)) {
+ acm_doms_restore_ssidref();
+ }
+ do_chwall_init_state_curr(NULL);
write_unlock(&acm_bin_pol_rwlock);
+
+ return -EFAULT;
+}
+
+
+int
+do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy,
+ struct acm_sized_buffer *deletions,
+ struct acm_sized_buffer *ssidchanges,
+ struct acm_sized_buffer *errors)
+{
+ struct acm_policy_buffer *pol = (struct acm_policy_buffer *)buf;
+
+ /* some sanity checking */
+ if ((be32_to_cpu(pol->magic) != ACM_MAGIC) ||
+ (buf_size != be32_to_cpu(pol->len)) ||
+ (be32_to_cpu(pol->policy_version) != ACM_POLICY_VERSION))
+ {
+ printk("%s: ERROR in Magic, Version, or buf size.\n", __func__);
+ goto error_free;
+ }
+
+ if (acm_active_security_policy == ACM_POLICY_UNDEFINED) {
+ /* setup the policy with the boot policy */
+ if (acm_init_binary_policy((be32_to_cpu(pol->secondary_policy_code) << 4) |
+ be32_to_cpu(pol->primary_policy_code))) {
+ goto error_free;
+ }
+ acm_active_security_policy = (acm_bin_pol.secondary_policy_code << 4) |
+ acm_bin_pol.primary_policy_code;
+ }
+
+ /* once acm_active_security_policy is set, it cannot be changed */
+ if ((be32_to_cpu(pol->primary_policy_code) != acm_bin_pol.primary_policy_code) ||
+ (be32_to_cpu(pol->secondary_policy_code) != acm_bin_pol.secondary_policy_code))
+ {
+ printkd("%s: Wrong policy type in boot policy!\n", __func__);
+ goto error_free;
+ }
+
+ return _acm_update_policy(buf, buf_size, is_bootpolicy,
+ pol,
+ deletions, ssidchanges,
+ errors);
+
error_free:
printk("%s: Error setting policy.\n", __func__);
return -EFAULT;
bin_pol->policy_reference_offset = cpu_to_be32(be32_to_cpu(bin_pol->len));
bin_pol->primary_buffer_offset = cpu_to_be32(be32_to_cpu(bin_pol->len));
bin_pol->secondary_buffer_offset = cpu_to_be32(be32_to_cpu(bin_pol->len));
-
+
memcpy(&bin_pol->xml_pol_version,
&acm_bin_pol.xml_pol_version,
sizeof(struct acm_policy_version));
return ret;
}
+
+
+/*
+ Check if an ssidref of the current policy type is being used by any
+ domain.
+ */
+static int
+acm_check_used_ssidref(uint32_t policy_type, uint32_t search_ssidref,
+ struct acm_sized_buffer *errors)
+{
+ int rc = 0;
+ struct acm_ssid_domain *rawssid;
+
+ read_lock(&ssid_list_rwlock);
+
+ for_each_acmssid( rawssid ) {
+ ssidref_t ssidref;
+ void *s = GET_SSIDP(policy_type, rawssid);
+
+ if (policy_type == ACM_CHINESE_WALL_POLICY) {
+ ssidref = ((struct chwall_ssid *)s)->chwall_ssidref;
+ } else {
+ ssidref = ((struct ste_ssid *)s)->ste_ssidref;
+ }
+ gdprintk(XENLOG_INFO,"domid=%d: search ssidref=%d, ssidref=%d\n",
+ rawssid->domainid,search_ssidref,ssidref);
+ if (ssidref == search_ssidref) {
+ /* one is enough */
+ acm_array_append_tuple(errors, ACM_SSIDREF_IN_USE, search_ssidref);
+ rc = 1;
+ break;
+ }
+ }
+
+ read_unlock(&ssid_list_rwlock);
+
+ return rc;
+}
+
+
+/*
+ * Translate a current ssidref into its future representation under
+ * the new policy.
+ * The map provides translation of ssidrefs from old to new in tuples
+ * of (old ssidref, new ssidref).
+ */
+static ssidref_t
+oldssid_to_newssid(const struct acm_ssid_domain *rawssid,
+ const struct acm_sized_buffer *map)
+{
+ uint i;
+
+ if (rawssid != NULL) {
+ ssidref_t ssid = rawssid->ssidref & 0xffff;
+ for (i = 0; i+1 < map->num_items; i += 2) {
+ if (map->array[i] == ssid) {
+ return (map->array[i+1] << 16 | map->array[i+1]);
+ }
+ }
+ }
+ return ACM_INVALID_SSIDREF;
+}
+
+
+/*
+ * Assign an ssidref to the CHWALL policy component of the domain
+ */
+static void
+acm_pri_policy_assign_ssidref(struct acm_ssid_domain *rawssid, ssidref_t new_ssid)
+{
+ struct chwall_ssid *chwall = (struct chwall_ssid *)rawssid->primary_ssid;
+ chwall->chwall_ssidref = new_ssid;
+}
+
+
+/*
+ * Assign an ssidref to the STE policy component of the domain
+ */
+static void
+acm_sec_policy_assign_ssidref(struct acm_ssid_domain *rawssid, ssidref_t new_ssid)
+{
+ struct ste_ssid *ste = (struct ste_ssid *)rawssid->secondary_ssid;
+ ste->ste_ssidref = new_ssid;
+}
+
+/*
+ Change the ssidrefs on each domain using a passed translation function;
+ */
+static void
+acm_doms_change_ssidref(ssidref_t (*translator_fn)
+ (const struct acm_ssid_domain *,
+ const struct acm_sized_buffer *),
+ struct acm_sized_buffer *translation_map)
+{
+ struct acm_ssid_domain *rawssid;
+
+ write_lock(&ssid_list_rwlock);
+
+ for_each_acmssid( rawssid ) {
+ ssidref_t new_ssid;
+
+ rawssid->old_ssidref = rawssid->ssidref;
+
+ new_ssid = translator_fn(rawssid, translation_map);
+ if (new_ssid == ACM_INVALID_SSIDREF) {
+ /* means no mapping found, so no change -- old = new */
+ continue;
+ }
+
+ acm_pri_policy_assign_ssidref(rawssid, ACM_PRIMARY (new_ssid) );
+ acm_sec_policy_assign_ssidref(rawssid, ACM_SECONDARY(new_ssid) );
+
+ rawssid->ssidref = new_ssid;
+ }
+
+ write_unlock(&ssid_list_rwlock);
+}
+
+/*
+ * Restore the previous ssidref values on all domains
+ */
+static void
+acm_doms_restore_ssidref(void)
+{
+ struct acm_ssid_domain *rawssid;
+
+ write_lock(&ssid_list_rwlock);
+
+ for_each_acmssid( rawssid ) {
+ ssidref_t old_ssid;
+
+ if (rawssid->old_ssidref == rawssid->ssidref)
+ continue;
+
+ old_ssid = rawssid->old_ssidref & 0xffff;
+ rawssid->ssidref = rawssid->old_ssidref;
+
+ acm_pri_policy_assign_ssidref(rawssid, old_ssid);
+ acm_sec_policy_assign_ssidref(rawssid, old_ssid);
+ }
+
+ write_unlock(&ssid_list_rwlock);
+}
+
+
+/*
+ Check the list of domains whether either one of them uses a
+ to-be-deleted ssidref.
+ */
+static int
+acm_check_deleted_ssidrefs(struct acm_sized_buffer *dels,
+ struct acm_sized_buffer *errors)
+{
+ int rc = 0;
+ uint idx;
+ /* check for running domains that should not be there anymore */
+ for (idx = 0; idx < dels->num_items; idx++) {
+ if (acm_check_used_ssidref(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY,
+ dels->array[idx],
+ errors) > 0 ||
+ acm_check_used_ssidref(ACM_CHINESE_WALL_POLICY,
+ dels->array[idx],
+ errors) > 0) {
+ rc = ACM_ERROR;
+ break;
+ }
+ }
+ return rc;
+}
+
+
+/*
+ * Change the policy of the system.
+ */
+int
+acm_change_policy(struct acm_change_policy *chgpolicy)
+{
+ int rc = 0;
+ u8 *binpolicy = NULL;
+ struct acm_sized_buffer dels = {
+ .array = NULL,
+ };
+ struct acm_sized_buffer ssidmap = {
+ .array = NULL,
+ };
+ struct acm_sized_buffer errors = {
+ .array = NULL,
+ };
+
+ gdprintk(XENLOG_INFO, "change policy operation\n");
+
+ if ((chgpolicy->delarray_size > 4096) ||
+ (chgpolicy->chgarray_size > 4096) ||
+ (chgpolicy->errarray_size > 4096)) {
+ return ACM_ERROR;
+ }
+
+ dels.num_items = chgpolicy->delarray_size / sizeof(uint32_t);
+ if (dels.num_items > 0) {
+ dels.array = xmalloc_array(uint32_t, dels.num_items);
+ if (dels.array == NULL) {
+ rc = -ENOMEM;
+ goto acm_chg_policy_exit;
+ }
+ }
+
+ ssidmap.num_items = chgpolicy->chgarray_size / sizeof(uint32_t);
+ if (ssidmap.num_items > 0) {
+ ssidmap.array = xmalloc_array(uint32_t, ssidmap.num_items);
+ if (ssidmap.array == NULL) {
+ rc = -ENOMEM;
+ goto acm_chg_policy_exit;
+ }
+ }
+
+ errors.num_items = chgpolicy->errarray_size / sizeof(uint32_t);
+ if (errors.num_items > 0) {
+ errors.array = xmalloc_array(uint32_t, errors.num_items);
+ if (errors.array == NULL) {
+ rc = -ENOMEM;
+ goto acm_chg_policy_exit;
+ }
+ memset(errors.array, 0x0, sizeof(uint32_t) * errors.num_items);
+ }
+
+ binpolicy = xmalloc_array(u8,
+ chgpolicy->policy_pushcache_size);
+ if (binpolicy == NULL) {
+ rc = -ENOMEM;
+ goto acm_chg_policy_exit;
+ }
+
+ if ( copy_from_guest(dels.array,
+ chgpolicy->del_array,
+ chgpolicy->delarray_size) ||
+ copy_from_guest(ssidmap.array,
+ chgpolicy->chg_array,
+ chgpolicy->chgarray_size) ||
+ copy_from_guest(binpolicy,
+ chgpolicy->policy_pushcache,
+ chgpolicy->policy_pushcache_size )) {
+ rc = -EFAULT;
+ goto acm_chg_policy_exit;
+ }
+
+ rc = do_acm_set_policy(binpolicy,
+ chgpolicy->policy_pushcache_size,
+ 0,
+ &dels, &ssidmap, &errors);
+
+ if ( (errors.num_items > 0) &&
+ copy_to_guest(chgpolicy->err_array,
+ errors.array,
+ errors.num_items ) ) {
+ rc = -EFAULT;
+ goto acm_chg_policy_exit;
+ }
+
+
+acm_chg_policy_exit:
+ xfree(dels.array);
+ xfree(ssidmap.array);
+ xfree(errors.array);
+ xfree(binpolicy);
+
+ return rc;
+}
+
+
+/*
+ * Lookup the new ssidref given the domain's id.
+ * The translation map provides a list of tuples in the format
+ * (domid, new ssidref).
+ */
+static ssidref_t
+domid_to_newssid(const struct acm_ssid_domain *rawssid,
+ const struct acm_sized_buffer *map)
+{
+ domid_t domid = rawssid->domainid;
+ uint i;
+ for (i = 0; (i+1) < map->num_items; i += 2) {
+ if (map->array[i] == domid) {
+ return (ssidref_t)map->array[i+1];
+ }
+ }
+ return ACM_INVALID_SSIDREF;
+}
+
+
+int
+do_acm_relabel_doms(struct acm_sized_buffer *relabel_map,
+ struct acm_sized_buffer *errors)
+{
+ int rc = 0, irc;
+
+ write_lock(&acm_bin_pol_rwlock);
+
+ acm_doms_change_ssidref(domid_to_newssid, relabel_map);
+
+ /* run tests; collect as much error info as possible */
+ irc = do_chwall_init_state_curr(errors);
+ irc += do_ste_init_state_curr(errors);
+ if (irc != 0) {
+ rc = -EFAULT;
+ goto acm_relabel_doms_lock_err_exit;
+ }
+
+ write_unlock(&acm_bin_pol_rwlock);
+
+ return rc;
+
+acm_relabel_doms_lock_err_exit:
+ /* revert the new ssidref assignment */
+ acm_doms_restore_ssidref();
+ do_chwall_init_state_curr(NULL);
+
+ write_unlock(&acm_bin_pol_rwlock);
+
+ return rc;
+}
+
+
+int
+acm_relabel_domains(struct acm_relabel_doms *relabel)
+{
+ int rc = ACM_OK;
+ struct acm_sized_buffer relabels = {
+ .array = NULL,
+ };
+ struct acm_sized_buffer errors = {
+ .array = NULL,
+ };
+
+ if (relabel->relabel_map_size > 4096) {
+ return ACM_ERROR;
+ }
+
+ relabels.num_items = relabel->relabel_map_size / sizeof(uint32_t);
+ if (relabels.num_items > 0) {
+ relabels.array = xmalloc_array(uint32_t, relabels.num_items);
+ if (relabels.array == NULL) {
+ rc = -ENOMEM;
+ goto acm_relabel_doms_exit;
+ }
+ }
+
+ errors.num_items = relabel->errarray_size / sizeof(uint32_t);
+ if (errors.num_items > 0) {
+ errors.array = xmalloc_array(uint32_t, errors.num_items);
+ if (errors.array == NULL) {
+ rc = -ENOMEM;
+ goto acm_relabel_doms_exit;
+ }
+ memset(errors.array, 0x0, sizeof(uint32_t) * errors.num_items);
+ }
+
+ if ( copy_from_guest(relabels.array,
+ relabel->relabel_map,
+ relabel->relabel_map_size) ) {
+ rc = -EFAULT;
+ goto acm_relabel_doms_exit;
+ }
+
+ rc = do_acm_relabel_doms(&relabels, &errors);
+
+ if ( copy_to_guest(relabel->err_array,
+ errors.array,
+ errors.num_items ) ) {
+ rc = -EFAULT;
+ goto acm_relabel_doms_exit;
+ }
+
+acm_relabel_doms_exit:
+ xfree(relabels.array);
+ xfree(errors.array);
+ return rc;
+}
+
/*
* Local variables:
* mode: C
* (right now: event_channels, future: also grant_tables)
*/
static int
-ste_init_state(struct acm_ste_policy_buffer *ste_buf, domaintype_t *ssidrefs)
+ste_init_state(struct acm_sized_buffer *errors)
{
int violation = 1;
struct ste_ssid *ste_ssid, *ste_rssid;
int port, i;
rcu_read_lock(&domlist_read_lock);
- /* go by domain? or directly by global? event/grant list */
+ read_lock(&ssid_list_rwlock);
/* go through all domains and adjust policy as if this domain was started now */
for_each_domain ( d )
{
"%x -> domain %x.\n",
__func__, d->domain_id, rdomid);
spin_unlock(&d->evtchn_lock);
+
+ acm_array_append_tuple(errors,
+ ACM_EVTCHN_SHARING_VIOLATION,
+ d->domain_id << 16 | rdomid);
goto out;
}
}
spin_unlock(&d->evtchn_lock);
}
+
/* b) check for grant table conflicts on shared pages */
spin_lock(&d->grant_table->lock);
for ( i = 0; i < nr_active_grant_frames(d->grant_table); i++ ) {
if ((rdom = rcu_lock_domain_by_id(rdomid)) == NULL) {
spin_unlock(&d->grant_table->lock);
printkd("%s: domain not found ERROR!\n", __func__);
+
+ acm_array_append_tuple(errors,
+ ACM_DOMAIN_LOOKUP,
+ rdomid);
goto out;
}
/* rdom now has remote domain */
printkd("%s: Policy violation in grant table "
"sharing domain %x -> domain %x.\n",
__func__, d->domain_id, rdomid);
+
+ acm_array_append_tuple(errors,
+ ACM_GNTTAB_SHARING_VIOLATION,
+ d->domain_id << 16 | rdomid);
goto out;
}
}
}
violation = 0;
out:
+ read_unlock(&ssid_list_rwlock);
rcu_read_unlock(&domlist_read_lock);
return violation;
/* returning "violation != 0" means that existing sharing between domains would not
* type in their typesets referenced by their ssidrefs */
}
+
+/*
+ * Call ste_init_state with the current policy.
+ */
+int
+do_ste_init_state_curr(struct acm_sized_buffer *errors)
+{
+ return ste_init_state(errors);
+}
+
+
/* set new policy; policy write-locked already */
static int
-ste_set_policy(u8 *buf, u32 buf_size, int is_bootpolicy)
+_ste_update_policy(u8 *buf, u32 buf_size, int test_only,
+ struct acm_sized_buffer *errors)
{
+ int rc = -EFAULT;
struct acm_ste_policy_buffer *ste_buf = (struct acm_ste_policy_buffer *)buf;
void *ssidrefsbuf;
struct ste_ssid *ste_ssid;
- struct domain *d;
+ struct acm_ssid_domain *rawssid;
int i;
- if (buf_size < sizeof(struct acm_ste_policy_buffer))
- return -EINVAL;
-
- /* Convert endianess of policy */
- ste_buf->policy_code = be32_to_cpu(ste_buf->policy_code);
- ste_buf->policy_version = be32_to_cpu(ste_buf->policy_version);
- ste_buf->ste_max_types = be32_to_cpu(ste_buf->ste_max_types);
- ste_buf->ste_max_ssidrefs = be32_to_cpu(ste_buf->ste_max_ssidrefs);
- ste_buf->ste_ssid_offset = be32_to_cpu(ste_buf->ste_ssid_offset);
-
- /* policy type and version checks */
- if ((ste_buf->policy_code != ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) ||
- (ste_buf->policy_version != ACM_STE_VERSION))
- return -EINVAL;
/* 1. create and copy-in new ssidrefs buffer */
ssidrefsbuf = xmalloc_array(u8, sizeof(domaintype_t)*ste_buf->ste_max_types*ste_buf->ste_max_ssidrefs);
if (ste_buf->ste_ssid_offset + sizeof(domaintype_t) * ste_buf->ste_max_ssidrefs*ste_buf->ste_max_types > buf_size)
goto error_free;
- /* during boot dom0_chwall_ssidref is set */
- if (is_bootpolicy && (dom0_ste_ssidref >= ste_buf->ste_max_ssidrefs)) {
- goto error_free;
- }
-
arrcpy(ssidrefsbuf,
buf + ste_buf->ste_ssid_offset,
sizeof(domaintype_t),
ste_buf->ste_max_ssidrefs*ste_buf->ste_max_types);
- /* 2. now re-calculate sharing decisions based on running domains;
+
+ /* 3. in test mode: re-calculate sharing decisions based on running domains;
* this can fail if new policy is conflicting with sharing of running domains
* now: reject violating new policy; future: adjust sharing through revoking sharing */
- if (ste_init_state(ste_buf, (domaintype_t *)ssidrefsbuf)) {
- printk("%s: New policy conflicts with running domains. Policy load aborted.\n", __func__);
- goto error_free; /* new policy conflicts with sharing of running domains */
+
+ if (test_only) {
+ /* temporarily replace old policy with new one for the testing */
+ struct ste_binary_policy orig_ste_bin_pol = ste_bin_pol;
+ ste_bin_pol.max_types = ste_buf->ste_max_types;
+ ste_bin_pol.max_ssidrefs = ste_buf->ste_max_ssidrefs;
+ ste_bin_pol.ssidrefs = (domaintype_t *)ssidrefsbuf;
+
+ if (ste_init_state(NULL)) {
+ /* new policy conflicts with sharing of running domains */
+ printk("%s: New policy conflicts with running domains. "
+ "Policy load aborted.\n", __func__);
+ } else {
+ rc = ACM_OK;
+ }
+ /* revert changes, no matter whether testing was successful or not */
+ ste_bin_pol = orig_ste_bin_pol;
+ goto error_free;
}
+
/* 3. replace old policy (activate new policy) */
ste_bin_pol.max_types = ste_buf->ste_max_types;
ste_bin_pol.max_ssidrefs = ste_buf->ste_max_ssidrefs;
ste_bin_pol.ssidrefs = (domaintype_t *)ssidrefsbuf;
/* clear all ste caches */
- rcu_read_lock(&domlist_read_lock);
- for_each_domain ( d ) {
- ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY,
- (struct acm_ssid_domain *)(d)->ssid);
+ read_lock(&ssid_list_rwlock);
+
+ for_each_acmssid( rawssid ) {
+ ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, rawssid);
for (i=0; i<ACM_TE_CACHE_SIZE; i++)
ste_ssid->ste_cache[i].valid = ACM_STE_free;
}
- rcu_read_unlock(&domlist_read_lock);
+
+ read_unlock(&ssid_list_rwlock);
+
return ACM_OK;
error_free:
- printk("%s: ERROR setting policy.\n", __func__);
+ if (!test_only) printk("%s: ERROR setting policy.\n", __func__);
xfree(ssidrefsbuf);
- return -EFAULT;
+ return rc;
+}
+
+static int
+ste_test_policy(u8 *buf, u32 buf_size, int is_bootpolicy,
+ struct acm_sized_buffer *errors)
+{
+ struct acm_ste_policy_buffer *ste_buf =
+ (struct acm_ste_policy_buffer *)buf;
+
+ if (buf_size < sizeof(struct acm_ste_policy_buffer))
+ return -EINVAL;
+
+ /* Convert endianess of policy */
+ ste_buf->policy_code = be32_to_cpu(ste_buf->policy_code);
+ ste_buf->policy_version = be32_to_cpu(ste_buf->policy_version);
+ ste_buf->ste_max_types = be32_to_cpu(ste_buf->ste_max_types);
+ ste_buf->ste_max_ssidrefs = be32_to_cpu(ste_buf->ste_max_ssidrefs);
+ ste_buf->ste_ssid_offset = be32_to_cpu(ste_buf->ste_ssid_offset);
+
+ /* policy type and version checks */
+ if ((ste_buf->policy_code != ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) ||
+ (ste_buf->policy_version != ACM_STE_VERSION))
+ return -EINVAL;
+
+ /* during boot dom0_chwall_ssidref is set */
+ if (is_bootpolicy && (dom0_ste_ssidref >= ste_buf->ste_max_ssidrefs))
+ return -EINVAL;
+
+ return _ste_update_policy(buf, buf_size, 1, errors);
+}
+
+static int
+ste_set_policy(u8 *buf, u32 buf_size)
+{
+ return _ste_update_policy(buf, buf_size, 0, NULL);
}
static int
{
struct ste_ssid *ste_ssid;
int i;
- struct domain *d;
- struct acm_ssid_domain *ssid;
+ struct acm_ssid_domain *rawssid;
printkd("deleting cache for dom %x.\n", id);
- rcu_read_lock(&domlist_read_lock);
+ read_lock(&ssid_list_rwlock);
/* look through caches of all domains */
- for_each_domain ( d ) {
- ssid = (struct acm_ssid_domain *)(d->ssid);
- if (ssid == NULL)
- continue; /* hanging domain structure, no ssid any more ... */
- ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssid);
+ for_each_acmssid ( rawssid ) {
+
+ ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, rawssid);
if (!ste_ssid) {
printk("%s: deleting ID from cache ERROR (no ste_ssid)!\n",
__func__);
(ste_ssid->ste_cache[i].id == id))
ste_ssid->ste_cache[i].valid = ACM_STE_free;
}
+
out:
- rcu_read_unlock(&domlist_read_lock);
+ read_unlock(&ssid_list_rwlock);
}
/***************************
.init_domain_ssid = ste_init_domain_ssid,
.free_domain_ssid = ste_free_domain_ssid,
.dump_binary_policy = ste_dump_policy,
+ .test_binary_policy = ste_test_policy,
.set_binary_policy = ste_set_policy,
.dump_statistics = ste_dump_stats,
.dump_ssid_types = ste_dump_ssid_types,
break;
}
+ case ACMOP_chgpolicy: {
+ struct acm_change_policy chgpolicy;
+
+ if (copy_from_guest(&chgpolicy, arg, 1) != 0)
+ return -EFAULT;
+ if (chgpolicy.interface_version != ACM_INTERFACE_VERSION)
+ return -EACCES;
+
+ rc = acm_change_policy(&chgpolicy);
+
+ if (rc == 0) {
+ if (copy_to_guest(arg, &chgpolicy, 1) != 0) {
+ rc = -EFAULT;
+ }
+ }
+ break;
+ }
+
+ case ACMOP_relabeldoms: {
+ struct acm_relabel_doms relabeldoms;
+
+ if (copy_from_guest(&relabeldoms, arg, 1) != 0)
+ return -EFAULT;
+ if (relabeldoms.interface_version != ACM_INTERFACE_VERSION)
+ return -EACCES;
+
+ rc = acm_relabel_domains(&relabeldoms);
+
+ if (rc == 0) {
+ if (copy_to_guest(arg, &relabeldoms, 1) != 0) {
+ rc = -EFAULT;
+ }
+ }
+ break;
+ }
+
default:
rc = -ENOSYS;
break;
#define _ACM_CORE_H
#include <xen/spinlock.h>
+#include <xen/list.h>
#include <public/acm.h>
#include <xen/acm_policy.h>
#include <public/acm_ops.h>
+#include <acm/acm_endian.h>
/* Xen-internal representation of the binary policy */
struct acm_binary_policy {
extern struct ste_binary_policy ste_bin_pol;
/* use the lock when reading / changing binary policy ! */
extern rwlock_t acm_bin_pol_rwlock;
+extern rwlock_t ssid_list_rwlock;
/* subject and object type definitions */
#define ACM_DATATYPE_domain 1
/* general definition of a subject security id */
struct acm_ssid_domain {
- int datatype; /* type of subject (e.g., partition): ACM_DATATYPE_* */
- ssidref_t ssidref; /* combined security reference */
- void *primary_ssid; /* primary policy ssid part (e.g. chinese wall) */
- void *secondary_ssid; /* secondary policy ssid part (e.g. type enforcement) */
- struct domain *subject; /* backpointer to subject structure */
- domid_t domainid; /* replicate id */
+ struct list_head node; /* all are chained together */
+ int datatype; /* type of subject (e.g., partition): ACM_DATATYPE_* */
+ ssidref_t ssidref; /* combined security reference */
+ ssidref_t old_ssidref; /* holds previous value of ssidref during relabeling */
+ void *primary_ssid; /* primary policy ssid part (e.g. chinese wall) */
+ void *secondary_ssid; /* secondary policy ssid part (e.g. type enforcement) */
+ struct domain *subject;/* backpointer to subject structure */
+ domid_t domainid; /* replicate id */
};
/* chinese wall ssid type */
((POLICY) == acm_bin_pol.primary_policy_code) ? \
((ssid)->primary_ssid) : ((ssid)->secondary_ssid)
+#define ACM_INVALID_SSIDREF (0xffffffff)
+
+struct acm_sized_buffer
+{
+ uint32_t *array;
+ uint num_items;
+ uint position;
+};
+
+static inline int acm_array_append_tuple(struct acm_sized_buffer *buf,
+ uint32_t a, uint32_t b)
+{
+ uint i;
+ if (buf == NULL)
+ return 0;
+
+ i = buf->position;
+
+ if ((i + 2) > buf->num_items)
+ return 0;
+
+ buf->array[i] = cpu_to_be32(a);
+ buf->array[i+1] = cpu_to_be32(b);
+ buf->position += 2;
+ return 1;
+}
+
/* protos */
int acm_init_domain_ssid(domid_t id, ssidref_t ssidref);
int acm_init_domain_ssid_new(struct domain *, ssidref_t ssidref);
void acm_free_domain_ssid(struct acm_ssid_domain *ssid);
int acm_init_binary_policy(u32 policy_code);
int acm_set_policy(XEN_GUEST_HANDLE(void) buf, u32 buf_size);
-int do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy);
+int do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy,
+ struct acm_sized_buffer *, struct acm_sized_buffer *,
+ struct acm_sized_buffer *);
int acm_get_policy(XEN_GUEST_HANDLE(void) buf, u32 buf_size);
int acm_dump_statistics(XEN_GUEST_HANDLE(void) buf, u16 buf_size);
int acm_get_ssid(ssidref_t ssidref, XEN_GUEST_HANDLE(void) buf, u16 buf_size);
int acm_get_decision(ssidref_t ssidref1, ssidref_t ssidref2, u32 hook);
int acm_set_policy_reference(u8 * buf, u32 buf_size);
int acm_dump_policy_reference(u8 *buf, u32 buf_size);
-
+int acm_change_policy(struct acm_change_policy *);
+int acm_relabel_domains(struct acm_relabel_doms *);
+int do_chwall_init_state_curr(struct acm_sized_buffer *);
+int do_ste_init_state_curr(struct acm_sized_buffer *);
/* variables */
extern ssidref_t dom0_chwall_ssidref;
extern ssidref_t dom0_ste_ssidref;
#define ACM_MAX_NUM_TYPES (256)
+/* traversing the list of ssids */
+extern struct list_head ssid_list;
+#define for_each_acmssid( N ) \
+ for ( N = (struct acm_ssid_domain *)ssid_list.next; \
+ N != (struct acm_ssid_domain *)&ssid_list; \
+ N = (struct acm_ssid_domain *)N->node.next )
+
#endif
/*
int (*init_domain_ssid) (void **ssid, ssidref_t ssidref);
void (*free_domain_ssid) (void *ssid);
int (*dump_binary_policy) (u8 *buffer, u32 buf_size);
- int (*set_binary_policy) (u8 *buffer, u32 buf_size,
- int is_bootpolicy);
+ int (*test_binary_policy) (u8 *buffer, u32 buf_size,
+ int is_bootpolicy,
+ struct acm_sized_buffer *);
+ int (*set_binary_policy) (u8 *buffer, u32 buf_size);
int (*dump_statistics) (u8 *buffer, u16 buf_size);
int (*dump_ssid_types) (ssidref_t ssidref, u8 *buffer, u16 buf_size);
/* domain management control hooks (can be NULL) */
}
+static inline void acm_domain_destroy(struct domain *d)
+{
+ void *ssid = d->ssid;
+ if (ssid != NULL) {
+ if (acm_primary_ops->domain_destroy != NULL)
+ acm_primary_ops->domain_destroy(ssid, d);
+ if (acm_secondary_ops->domain_destroy != NULL)
+ acm_secondary_ops->domain_destroy(ssid, d);
+ /* free security ssid for the destroyed domain (also if null policy */
+ acm_free_domain_ssid((struct acm_ssid_domain *)(ssid));
+ }
+}
+
+
static inline int acm_domain_create(struct domain *d, ssidref_t ssidref)
{
void *subject_ssid = current->domain->ssid;
domid_t domid = d->domain_id;
- int rc;
+ int rc = 0;
+ read_lock(&acm_bin_pol_rwlock);
/*
To be called when a domain is created; returns '0' if the
domain is allowed to be created, != '0' if not.
*/
- rc = acm_init_domain_ssid_new(d, ssidref);
- if (rc != ACM_OK)
- return rc;
if ((acm_primary_ops->domain_create != NULL) &&
acm_primary_ops->domain_create(subject_ssid, ssidref, domid)) {
- return ACM_ACCESS_DENIED;
+ rc = ACM_ACCESS_DENIED;
} else if ((acm_secondary_ops->domain_create != NULL) &&
acm_secondary_ops->domain_create(subject_ssid, ssidref,
domid)) {
if (acm_primary_ops->domain_destroy != NULL)
acm_primary_ops->domain_destroy(d->ssid, d);
acm_free_domain_ssid(d->ssid);
- return ACM_ACCESS_DENIED;
+ rc = ACM_ACCESS_DENIED;
}
- return 0;
-}
-
+ if (rc == 0) {
+ rc = acm_init_domain_ssid_new(d, ssidref);
-static inline void acm_domain_destroy(struct domain *d)
-{
- void *ssid = d->ssid;
- if (ssid != NULL) {
- if (acm_primary_ops->domain_destroy != NULL)
- acm_primary_ops->domain_destroy(ssid, d);
- if (acm_secondary_ops->domain_destroy != NULL)
- acm_secondary_ops->domain_destroy(ssid, d);
- /* free security ssid for the destroyed domain (also if null policy */
- acm_free_domain_ssid((struct acm_ssid_domain *)(ssid));
+ if (rc != ACM_OK) {
+ acm_domain_destroy(d);
+ }
}
+
+ read_unlock(&acm_bin_pol_rwlock);
+ return rc;
}
#define ACM_ACCESS_DENIED -111
#define ACM_NULL_POINTER_ERROR -200
+/*
+ Error codes reported in when trying to test for a new policy
+ These error codes are reported in an array of tuples where
+ each error code is followed by a parameter describing the error
+ more closely, such as a domain id.
+*/
+#define ACM_EVTCHN_SHARING_VIOLATION 0x100
+#define ACM_GNTTAB_SHARING_VIOLATION 0x101
+#define ACM_DOMAIN_LOOKUP 0x102
+#define ACM_CHWALL_CONFLICT 0x103
+#define ACM_SSIDREF_IN_USE 0x104
+
+
/* primary policy in lower 4 bits */
#define ACM_NULL_POLICY 0
#define ACM_CHINESE_WALL_POLICY 1
* This makes sure that old versions of acm tools will stop working in a
* well-defined way (rather than crashing the machine, for instance).
*/
-#define ACM_INTERFACE_VERSION 0xAAAA0008
+#define ACM_INTERFACE_VERSION 0xAAAA0009
/************************************************************************/
struct acm_setpolicy {
/* IN */
uint32_t interface_version;
- XEN_GUEST_HANDLE(void) pushcache;
+ XEN_GUEST_HANDLE_64(void) pushcache;
uint32_t pushcache_size;
};
struct acm_getpolicy {
/* IN */
uint32_t interface_version;
- XEN_GUEST_HANDLE(void) pullcache;
+ XEN_GUEST_HANDLE_64(void) pullcache;
uint32_t pullcache_size;
};
struct acm_dumpstats {
/* IN */
uint32_t interface_version;
- XEN_GUEST_HANDLE(void) pullcache;
+ XEN_GUEST_HANDLE_64(void) pullcache;
uint32_t pullcache_size;
};
domaintype_t domainid;
ssidref_t ssidref;
} id;
- XEN_GUEST_HANDLE(void) ssidbuf;
+ XEN_GUEST_HANDLE_64(void) ssidbuf;
uint32_t ssidbuf_size;
};
uint32_t acm_decision;
};
+
+#define ACMOP_chgpolicy 6
+struct acm_change_policy {
+ /* IN */
+ uint32_t interface_version;
+ XEN_GUEST_HANDLE_64(void) policy_pushcache;
+ uint32_t policy_pushcache_size;
+ XEN_GUEST_HANDLE_64(void) del_array;
+ uint32_t delarray_size;
+ XEN_GUEST_HANDLE_64(void) chg_array;
+ uint32_t chgarray_size;
+ /* OUT */
+ /* array with error code */
+ XEN_GUEST_HANDLE_64(void) err_array;
+ uint32_t errarray_size;
+};
+
+#define ACMOP_relabeldoms 7
+struct acm_relabel_doms {
+ /* IN */
+ uint32_t interface_version;
+ XEN_GUEST_HANDLE_64(void) relabel_map;
+ uint32_t relabel_map_size;
+ /* OUT */
+ XEN_GUEST_HANDLE_64(void) err_array;
+ uint32_t errarray_size;
+};
+
+/* future interface to Xen */
+struct xen_acmctl {
+ uint32_t cmd;
+ uint32_t interface_version;
+ union {
+ struct acm_setpolicy setpolicy;
+ struct acm_getpolicy getpolicy;
+ struct acm_dumpstats dumpstats;
+ struct acm_getssid getssid;
+ struct acm_getdecision getdecision;
+ struct acm_change_policy change_policy;
+ struct acm_relabel_doms relabel_doms;
+ } u;
+};
+
+typedef struct xen_acmctl xen_acmctl_t;
+DEFINE_XEN_GUEST_HANDLE(xen_acmctl_t);
+
#endif /* __XEN_PUBLIC_ACM_OPS_H__ */
/*